using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using Rotempco.Core.ORM.Objects;
using Rotempco.Core.ORM.Logic;
using System.Threading;
using Rotempco.Core.ORM.Managers;
using Rotempco.Core.ORM.Objects.Stores;
using Rotempco.Core.Logic;
using Rotempco.Core.ORM.Objects.Enums;
using System.Reflection;

namespace Rotempco.Core.ORM.ADAL
{
    public abstract class ADALReport<TDomainObject>
          where TDomainObject : class, new()
    {
        protected internal virtual List<ORMParameter> GetORMMapping()
        {
            List<ORMParameter> ormParameterList = new List<ORMParameter>();
            FillORMMapping(ormParameterList);

            return ormParameterList;
        }

        protected internal virtual void FillORMMapping(List<ORMParameter> ormParameterList)
        {
            PropertyInfo[] propertyInfos = typeof(TDomainObject).GetProperties();
            Dictionary<string, ORMParameter> ormParameterByPropertyName = ormParameterList.ToDictionary(o => o.PropertyName);
            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (!ormParameterByPropertyName.ContainsKey(propertyInfo.Name))
                {
                    ORMParameter ormParameter = new ORMParameter(propertyInfo.Name, new SymbolPair(propertyInfo.Name, propertyInfo.PropertyType));
                    ormParameterByPropertyName.Add(propertyInfo.Name, ormParameter);
                    ormParameterList.Add(ormParameter);
                }
            }
        }

        # region [ FillObject ]

        protected internal virtual TDomainObject FillObject(string sqlString, List<SqlParameter> sqlParameterList)
        {
            return FillObject(sqlString, sqlParameterList.ToArray());
        }

        protected internal TDomainObject FillObject(string sqlString)
        {
            return FillObject(sqlString, null as SqlParameter[]);
        }

        protected internal virtual TDomainObject FillObject(string sqlString, SqlParameter[] sqlParameters)
        {
            SqlConnection sqlConnection = DatabaseManager.Instance.SqlConnection;

            DataRow dataRow = DB.GetDataRow(sqlConnection, sqlString, sqlParameters);

            if (dataRow != null)
                return FillObject(dataRow);

            return null;
        }

        protected internal virtual TDomainObject FillObject(DataRow dataRow)
        {
            TDomainObject instance = new TDomainObject();

            foreach (ORMParameter ormParameter in GetORMMapping().Where(o => !o.IgnoreProperty))
            {
                object value = null;

                if (ormParameter.EncryptionType != EncryptionTypeEnum.ClearText && EncryptionStoreManager.Instance.IsEncryptionDisabled())
                    continue;
                else if (ormParameter.EncryptionType != EncryptionTypeEnum.ClearText || ormParameter.SerializableObject)
                {
                    if (ormParameter.EncryptionType == EncryptionTypeEnum.Symmetric)
                    {
                        if (string.IsNullOrWhiteSpace(EncryptionKeyStore.Instance.SymmetricEncryptionKey))
                            throw new Exception("Encryption key not defined");
                        value = SymmetricEncryptionLogic.DecryptObject(dataRow[ormParameter.ColumnName] + "", EncryptionKeyStore.Instance.SymmetricEncryptionKey);
                    }
                    else if (ormParameter.EncryptionType == EncryptionTypeEnum.ASymmetric)
                    {
                        if (string.IsNullOrWhiteSpace(EncryptionKeyStore.Instance.PrivateDecryptionKey))
                            throw new Exception("Private encryption key not defined");
                        value = AsymmetricEncryptionLogic.DecryptObject(dataRow[ormParameter.ColumnName] + "", EncryptionKeyStore.Instance.PrivateDecryptionKey);
                    }
                    else if (ormParameter.EncryptionType == EncryptionTypeEnum.RowBasedSymmetric)
                    {
                        throw new Exception("RowBased Encryption not posible in report mode");
                    }
                    else if (ormParameter.EncryptionType == EncryptionTypeEnum.Krust)
                    {
                        value = null;
                    }

                    if (ormParameter.SerializableObject && ormParameter.EncryptionType != EncryptionTypeEnum.Krust)
                        value = SerializerLogic.XmlDeserializeObject(ormParameter.Type, dataRow[ormParameter.ColumnName] + "", ormParameter.SerializableRootName);
                }
                else
                    value = dataRow[ormParameter.ColumnName];

                instance.GetType().GetProperty(ormParameter.PropertyName).SetValue(instance, DBNullHelper.ToType(value, ormParameter.Type), null);
            }

            return instance;
        }

        # endregion

        # region [ FillObjects ]

        protected internal List<TDomainObject> FillObjects(string sqlString)
        {
            return FillObjects(sqlString, null as SqlParameter[]);
        }

        protected internal List<TDomainObject> FillObjects(string sqlString, SqlParameter sqlParameter)
        {
            return FillObjects(sqlString, new SqlParameter[] { sqlParameter });
        }

        protected internal virtual List<TDomainObject> FillObjects(string sqlString, List<SqlParameter> sqlParameterList)
        {
            return FillObjects(sqlString, sqlParameterList.ToArray());
        }

        protected internal virtual List<TDomainObject> FillObjects(string sqlString, SqlParameter[] sqlParameters)
        {
            while (true)
            {

                string currentDatabase = DatabaseManager.Instance.GetCurrentDatabase();

                DataRow[] dataRows = DB.GetDataRows(DatabaseManager.Instance.SqlConnection, sqlString, sqlParameters);

                List<TDomainObject> result = new List<TDomainObject>();
                if (dataRows.Length > 0)
                {
                    foreach (DataRow dataRow in dataRows)
                    {
                        result.Add(FillObject(dataRow));
                    }
                }
                return result;
            }

        }

        # endregion

        protected internal virtual string GetTableColumns()
        {
            string selectSqlColumns = "";
            List<ORMParameter> ormMapping = GetORMMapping();

            foreach (ORMParameter ormParameter in ormMapping)
                selectSqlColumns += (selectSqlColumns == "" ? "" : ", ") + ormParameter.ColumnName + " ";

            return selectSqlColumns;
        }


        private int ThreadId
        {
            get
            {
                return Thread.CurrentThread.ManagedThreadId;
            }
        }

    }
}
